//Erik Wright
//marionette.cpp
//11-4-2013

/*
This file describes a robotic marionette puppet.  The goal of this file is to demonstrate 
a genetic algorithm that allows the puppet to 'learn' how to move.  This movement could be walking,
running, jumping, or really anything that comes to mind.  This particular algorithm will 
eventually be used to define the movements of the puppet as it dances.  Since there has already
been a lot of care going into the puppet, this functional description has been written to be as
fully descriptive as possible.

For more documentation on the motor controller for the puppet, see the headding of the bot_motion.h
file.
*/

#include "marionette.h"
#include <iostream>
using namespace std;



//---------------------------------------marionette_control-------------------------------------
//__________________private functions




//__________________public functions

//~marionette_control
/*
This function is the class deconstructor.  It will completely reallocate 
any initialized memory back on to the stack.
INPUT:	none
OUTPTU:	deconstruction
*/
marionette_control::~marionette_control()
{
	if(controller_array)		//an array has been allocated
	{
		int i = 0;			//iteration variable
		while(controller_array[i])	//visit each element of the list
		{
			delete controller_array[i];	//delete any alocated memory
			++i;				//update counter
		}
	}
}



//marionette_control(int, int)
/*
this is the default class constructor.  It will create an array of controller channels 
of length int channels and will initialize a list of events being int frames long.  
This can be scaled to any number of channels that a controller might have.
INPUT:	none
OUTPUT:	initialization
*/
marionette_control::marionette_control(int channels, int frames):
	rating(-1), num_controllers(channels)
{
	controller_array = new motion_event*[channels];
	motion_event* toins;

	for(int h=0; h<channels; h++)		//initialize every channel with at least the all stop command 
	{
		controller_array[h] = new motion_event(1500, 10, 10);	//initialize random value
		for(int i=0; i<frames; i++)
		{
			toins = new motion_event();
			controller_array[h]->insert_at_end(toins);	
		}

		toins = new motion_event(1500, 10, 10);	
		controller_array[h]->insert_at_end(toins);	//end with an all-stop command
	}
}




//find_num_controllers()
/*
This function returns the number of control channels present in the instance of 
the class.
INPUT:	none
OUTPUT:	int	| number of control channels
*/
int marionette_control::find_num_controllers()
{
	return num_controllers;
}



//display_sequence()
/*
this function provides standard output display for the current class instance.
This could be thought of a chromosome in a genetic algorithm.
INPUT:	none
OUTPUT:	standard display
*/
void marionette_control::display_sequence()
{
	if(controller_array)
	{
		int i = 0;	
		while(controller_array[i])
		{
			cout<<"Channel "<<i<<":\t";
			controller_array[i]->inorder_display();
			i++;
		}
	}
	else
	{
		cout<<"Controller_Array is NULL"<<endl;
	}
}



//give_rating()
/*
this function will set and reset the fitness of the collection of 
motion conrolls.  It will be used in the Evolutionary Algorithm to 
throw out the least desireable candidate for the mating process.
This also sets the fuzzy membership of this particular motion.
INPUT:	none
OUTPUT:	setting of variable through std in
*/
void marionette_control::give_rating()
{
	int temp = 0;
	bool flag = 1;

	while(flag)
	{
		cout<<"Please enter a rating between 0 and 100..."<<endl;
		cin>>temp;
		cout<<endl;

		if(temp<0 || temp>100)
		{
			cout<<"Please enter an acceptable rating..."<<endl;
		}
		else
		{
			flag = 0;
			rating = temp;

			//fuzzy membership
			if(temp <= 25)
			{
				fuzzy_membership[0] = ((-1/25)*temp) + 1;	//really bad membership
				fuzzy_membership[1] = ((1/25)*temp);		//bad membership
				fuzzy_membership[2] = 0;
				fuzzy_membership[3] = 0;
				fuzzy_membership[4] = 0;
			}
			if((temp > 25)&&(temp<=50))
			{
				fuzzy_membership[0] = 0;			//really bad membership
				fuzzy_membership[1] = ((-1/25)*temp)+2;		//bad membership
				fuzzy_membership[2] = ((1/25)*temp)-1;		//okay membership
				fuzzy_membership[3] = 0;
				fuzzy_membership[4] = 0;
			}
			if((temp > 50)&&(temp<=75))
			{
				fuzzy_membership[0] = 0;			//really bad membership
				fuzzy_membership[1] = 0;			//bad membership
				fuzzy_membership[2] = ((-1/25)*temp)+3;		//okay membership
				fuzzy_membership[3] = ((1/25)*temp)-2;		//good membership
				fuzzy_membership[4] = 0;
			}
			if((temp > 75)&&(temp<=100))
			{
				fuzzy_membership[0] = 0;			//really bad membership
				fuzzy_membership[1] = 0;			//bad membership
				fuzzy_membership[2] = 0;			//okay membership
				fuzzy_membership[3] = ((-1/25)*temp)+4;		//good membership
				fuzzy_membership[4] = ((1/25)*temp)-3;		//very good membership
			}
			return;
		}
	}	
}	



//get_rating()
/*
a function to return the current rating of the motion
INPUT:	none
OUTPUT:	int | the rating 
*/
int marionette_control::get_rating()
{
	return rating;
}



//binary_twist()
/*
This function allows the user to perform a binary chromosome tist
straight down the middle of every channel.  Twisting systems with 
different numbers of channels will not produce an error.  It will
only twist channels that correspond to system with the least
number of controllers.  Mixing systems with different lengths of
motion events will produce a varying number of lengths. 
INPUT:	marionette_control*&	| the system to be swapped with
OUTPUT:	void			| complete binary swap 
*/
void marionette_control::binary_twist(marionette_control*& given)
{
	int given_len = given->find_num_controllers();
	
	if((num_controllers == given_len)||(num_controllers < given_len))	//I am equal or less than in size
	{
		for(int i=0; i < num_controllers; i++)
		{
			controller_array[i]->binary_twist(given->controller_array[i], 1);
		}
	}
	if(num_controllers > given_len)		//sets are unequal; I am bigger
	{
		for(int i=0; i < given_len; i++)
		{
			controller_array[i]->binary_twist(given->controller_array[i], 1);
		}
	}
}



//binary_twist_rand()
/*
this function produces a binary swap with a random swap point in each list in the controller
array.  The keep_len will make sure that if the strings are equal sized, their resulting length
with be the same as their initial length.  If this flag is not set, they will result in different
lengths. 

INPUT: 	marionette_control *&	| the chomosome to swap with
	bool			| keep same length or not
OUTPUT:	void 
*/
void marionette_control::binary_twist_rand(marionette_control*& given, bool keep_len)
{
	int given_len = given->find_num_controllers();
	
	if((num_controllers == given_len)||(num_controllers < given_len))	//I am equal or less than in size
	{
		for(int i=0; i < num_controllers; i++)
		{
			controller_array[i]->binary_twist_rand(given->controller_array[i], keep_len);
		}
	}
	if(num_controllers > given_len)		//sets are unequal; I am bigger
	{
		for(int i=0; i < given_len; i++)
		{
			controller_array[i]->binary_twist_rand(given->controller_array[i], keep_len);
		}
	}
}



//fuzzy_twist()
/*
This function will implement a fuzzy twist.  Since fuzzy membership values have been set
upon setting the rating of the motion.  These membership values will be used to 
determine what is to be the fate of the two given motion lists.
INPUT:	marionette_control *& | given marionette control to twist with
OUTPUT:	int | if something needs to be deleted 
*/
int marionette_control::fuzzy_twist(marionette_control*& given)
{
	//I have broken the fuzzy membership into three other membership rules
	int todo_a = fuzzy_decision();
	int todo_b = fuzzy_decision();

	if((todo_a == 1) || (todo_b == 1))
	{
		if(todo_a == 1)
		{
			return 1;	//tell world to delete caller
		}
		if(todo_b == 1)
		{
			return 2;	//tell world to delete given
		}
	}
	if((todo_a > 1) && (todo_b > 1))
	{
		if((todo_a == 3) && (todo_b == 3))	
		{
			//average chromosomes
			average_twist(given);	//average the two 
			return 0;
		}
		else
		{
			//binary random twist
			binary_twist_rand(given, 1);	//maintain length
			return 0;
		}
	}

}



//fuzzy_decision()
/*
This function outlines the output of the different values of the
fuzzy memberships.  This corresponds to either wanting to delete
a chromosome because it has weak membership, performing a regular
binary twist to keep evolving those that have average membership,
or performing an average of two very top rated members as to preserve
their traits.
INPUT:	void
OUTPUT:	int	| output membership determined by fuzzy_membership array
*/


int marionette_control::fuzzy_decision()
{
	if(((fuzzy_membership[0]) >= (fuzzy_membership[1]))||((fuzzy_membership[1]) >= (fuzzy_membership[2])))	//really bad 
	{
		return 1;	//should delete the calling chromosome
	}
	if((fuzzy_membership[2]<fuzzy_membership[1])||
		(fuzzy_membership[2]>=fuzzy_membership[3])||
		(fuzzy_membership[2]<fuzzy_membership[3]))
	{
		return 2;	//perform binary twist
	}
	if((fuzzy_membership[3]<=fuzzy_membership[4])||(fuzzy_membership[4]>fuzzy_membership[3]))
	{
		return 3;	//perform average twist
	}
}



//average_twist()
/*
This function will traverse the array of linear linked lists
performing an average twist.  Be careful with using the average
twist function on a chromosome that has a different length.
This functionality is not supported yet.  The lengths of both
input lists should be the same. 
INPUT:	marionette_control *&	| input chromosome
OUTPUT:	void
*/
void marionette_control::average_twist(marionette_control*& given)
{
	int given_len = given->find_num_controllers();
	
	if((num_controllers == given_len)||(num_controllers < given_len))	//I am equal or less than in size
	{
		for(int i=0; i < num_controllers; i++)
		{
			controller_array[i]->average_twist(given->controller_array[i]);
		}
	}
	if(num_controllers > given_len)		//sets are unequal; I am bigger
	{
		for(int i=0; i < given_len; i++)
		{
			controller_array[i]->average_twist(given->controller_array[i]);
		}
	}
}
